package actor

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/asynkron/protoactor-go/actor"
	"github.com/google/uuid"
	"github.com/sirupsen/logrus"

	"xagent/internal/knowledge"
	"xagent/internal/memory"
	"xagent/internal/providers"
	proto "xagent/proto/v1"
)

// ActorState represents actor's state
type ActorState string

const (
	StateIdle     ActorState = "IDLE"
	StateWorking  ActorState = "WORKING"
	StatePaused   ActorState = "PAUSED"
	StateFinished ActorState = "FINISHED"
)

// ThoughtChain represents a chain of thoughts
type ThoughtChain struct {
	Thoughts []Thought
	mutex    sync.RWMutex
}

// Thought represents a single thought in the thinking process
type Thought struct {
	Content    string
	Reasoning  string
	Plan       []string
	Critical   string
	NextAction string
	Timestamp  time.Time
}

// LLMResponse represents a response from the LLM
type LLMResponse struct {
	Content    string
	Reasoning  string
	Plan       []string
	Critical   string
	NextAction string
}

// BaseActor represents a basic actor in the system
type BaseActor struct {
	actor.Actor
	ID           string
	Name         string
	Role         string
	Profile      string
	State        ActorState
	memory       memory.MemoryManager
	actions      map[string]Action
	capabilities []string
	context      actor.Context
	mutex        sync.RWMutex
	messageQueue chan *proto.AgentMessage
	supervisor   *actor.PID
	// New fields
	llm          providers.LLMProvider
	knowledge    *knowledge.RAGEngine
	thoughtChain *ThoughtChain
	logger       *logrus.Logger
}

// Action represents something an actor can do
type Action interface {
	Name() string
	Run(ctx context.Context, input interface{}) (*proto.AgentMessage, error)
	Prerequisites() []string
}

// NewBaseActor creates a new base actor
func NewBaseActor(name, role, profile string) *BaseActor {
	memMgr := memory.NewInMemoryManager()
	return &BaseActor{
		ID:           uuid.NewString(),
		Name:         name,
		Role:         role,
		Profile:      profile,
		State:        StateIdle,
		memory:       memMgr,
		actions:      make(map[string]Action),
		capabilities: make([]string, 0),
		messageQueue: make(chan *proto.AgentMessage, 100),
		thoughtChain: &ThoughtChain{
			Thoughts: make([]Thought, 0),
		},
		logger: logrus.New(),
	}
}

// Receive handles incoming messages
func (a *BaseActor) Receive(context actor.Context) {
	a.context = context
	a.handleSystemMessages(context)

	switch msg := context.Message().(type) {
	case *proto.AgentMessage:
		a.handleAgentMessage(msg)
	case *proto.Task:
		a.handleTask(msg)
	case *actor.Started:
		a.OnStarted()
	case *actor.Stopping:
		a.OnStopping()
	case *actor.Stopped:
		a.OnStopped()
	case *actor.Restarting:
		a.OnRestarting()
	}
}

// AddAction adds an action to the actor
func (a *BaseActor) AddAction(action Action) {
	a.mutex.Lock()
	defer a.mutex.Unlock()
	a.actions[action.Name()] = action
}

// AddCapability adds a capability to the actor
func (a *BaseActor) AddCapability(capability string) {
	a.mutex.Lock()
	defer a.mutex.Unlock()
	a.capabilities = append(a.capabilities, capability)
}

// GetCapabilities returns actor's capabilities
func (a *BaseActor) GetCapabilities() []string {
	a.mutex.RLock()
	defer a.mutex.RUnlock()
	return append([]string{}, a.capabilities...)
}

// handleAgentMessage processes agent messages
func (a *BaseActor) handleAgentMessage(msg *proto.AgentMessage) {
	a.mutex.Lock()
	a.State = StateWorking
	a.mutex.Unlock()

	defer func() {
		a.mutex.Lock()
		a.State = StateIdle
		a.mutex.Unlock()
	}()

	// Store message in memory
	entry := &memory.MemoryEntry{
		ID:      uuid.NewString(),
		Content: a.getMessageContent(msg),
		Type:    memory.MemoryTypeConversation,
		Metadata: map[string]interface{}{
			"sender": msg.Sender,
			"type":   msg.Type.String(),
		},
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}

	if err := a.memory.Store(context.Background(), entry); err != nil {
		a.handleError(err)
		return
	}

	// Process message based on type
	switch msg.Type {
	case proto.MessageType_MESSAGE_TYPE_REQUEST:
		a.handleRequest(msg)
	case proto.MessageType_MESSAGE_TYPE_RESPONSE:
		a.handleResponse(msg)
	case proto.MessageType_MESSAGE_TYPE_ERROR:
		a.handleErrorMessage(msg)
	}
}

// handleTask processes task messages with thinking
func (a *BaseActor) handleTask(task *proto.Task) {
	a.mutex.Lock()
	a.State = StateWorking
	a.mutex.Unlock()

	defer func() {
		a.mutex.Lock()
		a.State = StateIdle
		a.mutex.Unlock()
	}()

	// Think about the task
	thought, err := a.Think(context.Background(), task.Description)
	if err != nil {
		a.handleError(err)
		return
	}

	// Find and execute appropriate action based on thought
	if action, ok := a.actions[thought.NextAction]; ok {
		result, err := action.Run(context.Background(), task)
		if err != nil {
			a.handleError(err)
			return
		}
		a.context.Send(a.context.Parent(), result)
	}
}

// handleSystemMessages handles actor system messages
func (a *BaseActor) handleSystemMessages(context actor.Context) {
	switch context.Message().(type) {
	case *actor.Terminated:
		a.OnChildTerminated(context)
	case *actor.Started:
		a.OnStarted()
	case *actor.Stopping:
		a.OnStopping()
	case *actor.Stopped:
		a.OnStopped()
	case *actor.Restarting:
		a.OnRestarting()
	}
}

// Lifecycle hooks
func (a *BaseActor) OnStarted()                          { a.State = StateIdle }
func (a *BaseActor) OnStopping()                         { a.State = StatePaused }
func (a *BaseActor) OnStopped()                          { a.State = StateFinished }
func (a *BaseActor) OnRestarting()                       { a.State = StateIdle }
func (a *BaseActor) OnChildTerminated(ctx actor.Context) {}

// Helper methods
func (a *BaseActor) handleRequest(msg *proto.AgentMessage) {
	// Implementation depends on specific actor type
}

func (a *BaseActor) handleResponse(msg *proto.AgentMessage) {
	// Implementation depends on specific actor type
}

func (a *BaseActor) handleErrorMessage(msg *proto.AgentMessage) {
	// Implementation depends on specific actor type
}

func (a *BaseActor) handleError(err error) {
	if a.context != nil && a.context.Parent() != nil {
		errMsg := &proto.AgentMessage{
			Id:   uuid.NewString(),
			Type: proto.MessageType_MESSAGE_TYPE_ERROR,
			Content: &proto.AgentMessage_Error{
				Error: &proto.Error{
					Code:    "INTERNAL_ERROR",
					Message: err.Error(),
				},
			},
			Sender: a.Role,
		}
		a.context.Send(a.context.Parent(), errMsg)
	}
}

func (a *BaseActor) getMessageContent(msg *proto.AgentMessage) string {
	switch c := msg.Content.(type) {
	case *proto.AgentMessage_Message:
		return c.Message
	case *proto.AgentMessage_Error:
		return c.Error.Message
	default:
		return ""
	}
}

// GetContext returns the actor context
func (a *BaseActor) GetContext() actor.Context {
	return a.context
}

// GetRole returns the actor's role
func (a *BaseActor) GetRole() string {
	return a.Role
}

// GetState returns the actor's current state
func (a *BaseActor) GetState() ActorState {
	a.mutex.RLock()
	defer a.mutex.RUnlock()
	return a.State
}

// SetSupervisor sets the actor's supervisor
func (a *BaseActor) SetSupervisor(supervisor *actor.PID) {
	a.supervisor = supervisor
}

// GetSupervisor returns the actor's supervisor
func (a *BaseActor) GetSupervisor() *actor.PID {
	return a.supervisor
}

// Think generates a new thought based on the current context
func (a *BaseActor) Think(ctx context.Context, prompt string) (*Thought, error) {
	a.logger.WithFields(logrus.Fields{
		"role":   a.Role,
		"prompt": prompt,
	}).Debug("Thinking about prompt")

	// Build thinking prompt
	thinkingPrompt := fmt.Sprintf(`As a %s, analyze the following task and provide your thoughts:

Task: %s

Please structure your response as follows:
1. Initial thoughts and understanding
2. Reasoning process
3. Action plan (as a list of steps)
4. Potential issues or criticisms
5. Next immediate action to take

Consider your role's responsibilities and constraints while analyzing.`, a.Role, prompt)

	// Use LLM to generate thought
	response, err := a.llm.Complete(ctx, thinkingPrompt, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to generate thought: %w", err)
	}

	// Parse response into LLMResponse structure
	llmResponse := a.parseResponse(response)

	// Create thought from LLM response
	thought := &Thought{
		Content:    llmResponse.Content,
		Reasoning:  llmResponse.Reasoning,
		Plan:       llmResponse.Plan,
		Critical:   llmResponse.Critical,
		NextAction: llmResponse.NextAction,
		Timestamp:  time.Now(),
	}

	// Add to thought chain
	a.thoughtChain.mutex.Lock()
	a.thoughtChain.Thoughts = append(a.thoughtChain.Thoughts, *thought)
	a.thoughtChain.mutex.Unlock()

	return thought, nil
}

// parseResponse parses the LLM response into structured format
func (a *BaseActor) parseResponse(response string) *LLMResponse {
	// TODO: Implement proper parsing logic using regex or structured output
	// For now, return a simple structure
	return &LLMResponse{
		Content:    response,
		Reasoning:  "Parsed reasoning",
		Plan:       []string{"Step 1", "Step 2"},
		Critical:   "Potential issues",
		NextAction: "Next step",
	}
}

// GetThoughtChain returns the current thought chain
func (a *BaseActor) GetThoughtChain() []Thought {
	a.thoughtChain.mutex.RLock()
	defer a.thoughtChain.mutex.RUnlock()
	return append([]Thought{}, a.thoughtChain.Thoughts...)
}

// GetLastThought returns the last thought in the chain
func (a *BaseActor) GetLastThought() *Thought {
	a.thoughtChain.mutex.RLock()
	defer a.thoughtChain.mutex.RUnlock()
	if len(a.thoughtChain.Thoughts) == 0 {
		return nil
	}
	thought := a.thoughtChain.Thoughts[len(a.thoughtChain.Thoughts)-1]
	return &thought
}

// ClearThoughts clears the thought chain
func (a *BaseActor) ClearThoughts() {
	a.thoughtChain.mutex.Lock()
	defer a.thoughtChain.mutex.Unlock()
	a.thoughtChain.Thoughts = make([]Thought, 0)
}

// SetLLM sets the LLM provider
func (a *BaseActor) SetLLM(llm providers.LLMProvider) {
	a.llm = llm
}

// SetKnowledgeBase sets the knowledge base
func (a *BaseActor) SetKnowledgeBase(kb *knowledge.RAGEngine) {
	a.knowledge = kb
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
